/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package neuronalenetzeelearning.dataConnectors;

import com.sun.org.apache.xerces.internal.dom.DocumentImpl;
import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;
import java.io.IOException;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.ParserConfigurationException;
import neuronalenetzeelearning.model.Edge;
import neuronalenetzeelearning.model.Graph;
import neuronalenetzeelearning.model.Layer1Node;
import neuronalenetzeelearning.model.Node;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * This static class is for building GraphML representations of the Graph-Model that it can be used
 * as input for prefuse or other graph visualization libraries
 * @author svenmaster
 */
public class GraphMLDocumentBuilder {

    /**
     * Converts a Graph to GraphML
     * @param graph The Graph to be converted to an GraphML XML Document
     * @return The GraphML Document
     * @throws ParserConfigurationException
     * @throws SAXException
     * @throws MalformedURLException
     */
    public static Document convertToGraphML(Graph graph) throws ParserConfigurationException, SAXException, MalformedURLException {

        /*Example Graph in GraphML
         * <?xml version="1.0" encoding="UTF-8"?>
         * <graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
         *  <graph edgedefault="directed" id="graph">
         *  <key attr.name="name" attr.type="string" for="node" id="name"/>
         *  <key attr.name="threshold" attr.type="string" for="node" id="threshold"/>
         *  <node id="K1">
         *      <data key="name">K1</data>
         *      <data key="threshold">0.0</data>
         *  </node>
         *  <node id="K2">
         *      <data key="name">K2</data>
         *      <data key="threshold">0.0</data>
         *  </node>
         *  <edge id="e1" source="n0" target="n1"/>
         * </graph>
         * <graphml>
         */

        Document document = new DocumentImpl();
        document.setXmlVersion("1.0");
        //setting root and schema location for graphML
        Element root = document.createElement("graphml");
        root.setAttribute("xmlns", "http://graphml.graphdrawing.org/xmlns");
        root.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
        root.setAttribute("xsi:schemaLocation", "http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd");
        Element subRoot = document.createElement("graph");
        subRoot.setAttribute("id", "graph");
        subRoot.setAttribute("edgedefault", "directed");

        //defining data keys and types for the graph
        Element graphData1 = document.createElement("key");
        graphData1.setAttribute("id", "name");
        graphData1.setAttribute("for", "node");
        graphData1.setAttribute("attr.name", "name");
        graphData1.setAttribute("attr.type", "string");
        subRoot.appendChild(graphData1);

        Element graphData2 = document.createElement("key");
        graphData2.setAttribute("id", "threshold");
        graphData2.setAttribute("for", "node");
        graphData2.setAttribute("attr.name", "threshold");
        graphData2.setAttribute("attr.type", "string");
        subRoot.appendChild(graphData2);

        Element graphData3 = document.createElement("key");
        graphData3.setAttribute("id", "layer");
        graphData3.setAttribute("for", "node");
        graphData3.setAttribute("attr.name", "layer");
        graphData3.setAttribute("attr.type", "string");
        subRoot.appendChild(graphData3);

        Element graphData4 = document.createElement("key");
        graphData4.setAttribute("id", "xCoordinate");
        graphData4.setAttribute("for", "node");
        graphData4.setAttribute("attr.name", "xCoordinate");
        graphData4.setAttribute("attr.type", "integer");
        subRoot.appendChild(graphData4);

        Element graphData5 = document.createElement("key");
        graphData5.setAttribute("id", "yCoordinate");
        graphData5.setAttribute("for", "node");
        graphData5.setAttribute("attr.name", "yCoordinate");
        graphData5.setAttribute("attr.type", "integer");
        subRoot.appendChild(graphData5);

        root.appendChild(subRoot);

        //variables for coordinate calculations:
        int xLayer1 = 0;
        int xLayer2 = 0;
        int y = 0;

        //now append nodes of the graph
        for (Node node : graph.getNodes()) {
            Element nodeEl = document.createElement("node");
            nodeEl.setAttribute("id", node.getName());
//            nodeEl.setAttribute("threshold", String.valueOf(node.getThreshold()));

            Element dataNode1 = document.createElement("data");
            dataNode1.setTextContent(node.getName());
            dataNode1.setAttribute("key", "name");
            nodeEl.appendChild(dataNode1);

            Element dataNode2 = document.createElement("data");
            dataNode2.setTextContent(String.valueOf(node.getThreshold()));
            dataNode2.setAttribute("key", "threshold");
            nodeEl.appendChild(dataNode2);

            Element dataNode3 = document.createElement("data");
            dataNode3.setTextContent(node.getClass().getSimpleName());
            dataNode3.setAttribute("key", "layer");
            nodeEl.appendChild(dataNode3);

            //Setting coordinate-data for SpecifiedLayout (which is coordinate based)
            Vector<Node> nodes = graph.getNodes();
            int layer1NodeCount=graph.getLayer1Nodes().size();
            int layer2NodeCount = graph.getNodes().size() - layer1NodeCount;

            int width = 800;
            int heigth = 200;

            int xStepLayer1 = width / (layer1NodeCount+1);
            int xStepLayer2 = width / (layer2NodeCount+1);
            
            if (node instanceof Layer1Node) {
                Element dataNode4 = document.createElement("data");
                //Calculate the x-coordinate
                xLayer1 = xLayer1 + xStepLayer1;
                dataNode4.setTextContent(String.valueOf(xLayer1));
                dataNode4.setAttribute("key", "xCoordinate");
                nodeEl.appendChild(dataNode4);

                Element dataNode5 = document.createElement("data");
                y = 0;
                dataNode5.setTextContent(String.valueOf(y));
                dataNode5.setAttribute("key", "yCoordinate");
                nodeEl.appendChild(dataNode5);
            } else {
                Element dataNode4 = document.createElement("data");
                //Calculate the x-coordinate
                xLayer2 = xLayer2 + xStepLayer2;
                dataNode4.setTextContent(String.valueOf(xLayer2));
                dataNode4.setAttribute("key", "xCoordinate");
                nodeEl.appendChild(dataNode4);

                Element dataNode5 = document.createElement("data");
                y = heigth;
                dataNode5.setTextContent(String.valueOf(y));
                dataNode5.setAttribute("key", "yCoordinate");
                nodeEl.appendChild(dataNode5);
            }

            subRoot.appendChild(nodeEl);
        }

        //append edges of the graph
        for (Edge edge : graph.getEdges()) {
            Element edgeEl = document.createElement("edge");
            edgeEl.setAttribute("id", edge.getName());
            edgeEl.setAttribute("source", edge.getFrom().getName());
            edgeEl.setAttribute("target", edge.getTo().getName());
            subRoot.appendChild(edgeEl);
        }

        document.appendChild(root);
        return document;
    }

    /**
     * Converts a Graph to GraphML
     * @param graph The Graph to be converted to an GraphML XML Document
     * @return The GraphML Document
     */
    public static String convertToGraphMLAsString(Graph graph) {
        Document document = null;
        try {
            document = convertToGraphML(graph);
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(GraphMLDocumentBuilder.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SAXException ex) {
            Logger.getLogger(GraphMLDocumentBuilder.class.getName()).log(Level.SEVERE, null, ex);
        } catch (MalformedURLException ex) {
            Logger.getLogger(GraphMLDocumentBuilder.class.getName()).log(Level.SEVERE, null, ex);
        }
        //Declare outputFormat (the document)
        OutputFormat format = new OutputFormat(document);
        //Use a stringwriter for outputting a string
        StringWriter stringOut = new StringWriter();
        //Serializer
        XMLSerializer serializer = new XMLSerializer(stringOut,
                format);
        //Serialize DOM
        try {
            serializer.serialize(document);
        } catch (IOException ex) {
            Logger.getLogger(GraphMLDocumentBuilder.class.getName()).log(Level.SEVERE, null, ex);
        }
        return stringOut.toString();
    }

    public static Graph convertToGraph(Document graphML) {
        Graph graph = new Graph();

        //Create a table for mapping nodes of the Vector to nodeNames

        //parsing nodes from graphML to GraphNodes
        NodeList nodes = graphML.getElementsByTagName("node");
        for (int i = 0; i < nodes.getLength(); i++) {
            org.w3c.dom.Node domNode = nodes.item(i);
            //getting the attributes from the DOM-Node
            NamedNodeMap attributes = domNode.getAttributes();
            //Attribute order:
            //1. Name
            String name = attributes.item(0).getNodeName();
            System.out.println("name: " + name);
            //2. Threshold
            double threshold = Double.valueOf(attributes.item(1).getNodeName());
            System.out.println("threshold: " + threshold);

//            Node graphNode = new Node();
        }
        return graph;
    }
}
